"""Whisk Utility methods.

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
"""


import os
import json
import sys
if sys.version_info.major >= 3:
    from http.client import HTTPConnection, HTTPSConnection, IncompleteRead
    from urllib.parse import urlparse
else:
    from httplib import HTTPConnection, HTTPSConnection, IncompleteRead
    from urlparse import urlparse
import ssl
import base64
import socket


# global configurations, can control whether to allow untrusted certificates
# on HTTPS connections
httpRequestProps = {'secure': True}

def request(method, urlString, body = '', headers = {}, auth = None, verbose = False, https_proxy = os.getenv('https_proxy', None), timeout = 10):
    url = urlparse(urlString)
    if url.scheme == 'http':
        conn = HTTPConnection(url.netloc, timeout = timeout)
    else:
        if httpRequestProps['secure'] or not hasattr(ssl, '_create_unverified_context'):
            conn = HTTPSConnection(url.netloc if https_proxy is None else https_proxy, timeout = timeout)
        else:
            conn = HTTPSConnection(url.netloc if https_proxy is None else https_proxy, context=ssl._create_unverified_context(), timeout = timeout)
        if https_proxy:
            conn.set_tunnel(url.netloc)

    if auth is not None:
        auth = base64.b64encode(auth.encode()).decode()
        headers['Authorization'] = 'Basic %s' % auth

    if verbose:
        print('========')
        print('REQUEST:')
        print('%s %s' % (method, urlString))
        print('Headers sent:')
        print(getPrettyJson(headers))
        if body != '':
            print('Body sent:')
            print(body)

    try:
        conn.request(method, urlString, body, headers)
        res = conn.getresponse()
        body = ''
        try:
            body = res.read()
        except IncompleteRead as e:
            body = e.partial

        # patch the read to return just the body since the normal read
        # can only be done once
        res.read = lambda: body

        if verbose:
            print('--------')
            print('RESPONSE:')
            print('Got response with code %s' % res.status)
            print('Body received:')
            print(res.read())
            print('========')
        return res
    except socket.timeout:
        return ErrorResponse(status = 500, error = 'request timed out at %d seconds' % timeout)
    except Exception as e:
        return ErrorResponse(status = 500, error = str(e))


def getPrettyJson(obj):
    return json.dumps(obj, sort_keys=True, indent=4, separators=(',', ': '))


# class to normalize responses for exceptions with no HTTP response for canonical error handling
class ErrorResponse:
    def __init__(self, status, error):
        self.status = status
        self.error = error

    def read(self):
        return self.error
